#pragma once

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Diagnostics;
using namespace System::Text;


namespace ARSys { namespace Connector {

	/// <summary>
	/// Summary for ARUtil
	/// </summary>
	private ref class ARUtil abstract sealed
	{
	public:	
		static void ThrowARException(String^ arfunc, int err, ARStatusList* pStatus)
		{
			auto sb = gcnew System::Text::StringBuilder();

			sb->AppendFormat(L"Failed to call {0}", arfunc);
			sb->AppendFormat(L", HR=", err);
			sb->AppendFormat(L", Reason=");

			ARStatusList2Message(pStatus, sb);

			throw gcnew ARException(sb->ToString());
		}

	public:	
		static void ThrowARBulkException(String^ arfunc, int err, ARStatusList* pStatus, ARBulkEntryReturnList* bulkEntryReturnList)
		{
			auto sb = gcnew System::Text::StringBuilder();

			sb->AppendFormat(L"Failed to call {0}", arfunc);
			sb->AppendFormat(L", HR={0}", err);
			sb->AppendFormat(L", Reason=");

			ARStatusList2Message(pStatus, sb);

			sb->AppendFormat(L", Detailed Info=");

			for(int i=0; i<(int)bulkEntryReturnList->numItems; i++)
			{
				auto& ber = bulkEntryReturnList->entryReturnList[i];

				switch(ber.entryCallType)
				{
				case AR_BULK_ENTRY_CREATE:
					sb->AppendFormat(L", AR_BULK_ENTRY_CREATE: ");
					ARStatusList2Message(&ber.u.createEntryReturn.status, sb);
					break;
				case AR_BULK_ENTRY_SET:
					sb->AppendFormat(L", AR_BULK_ENTRY_SET: ");
					ARStatusList2Message(&ber.u.setEntryReturn, sb);
					break;
				case AR_BULK_ENTRY_DELETE:
					sb->AppendFormat(L", AR_BULK_ENTRY_DELETE: ");
					ARStatusList2Message(&ber.u.deleteEntryReturn, sb);
					break;
				default:
					break;
				}
			}

			throw gcnew ARException(sb->ToString());
		}

	public:
		static char* GetUTF8Text(String^ text)
		{
			if(String::IsNullOrEmpty(text))
				return NULL;
		
			array<Byte>^ vText = System::Text::Encoding::UTF8->GetBytes(text);
			pin_ptr<unsigned char> pText = &vText[0];

			char * szText = new char[vText->Length + 1];
			memcpy(szText, pText, vText->Length);
			szText[vText->Length] = '\0';
			return szText;		
		}

	private:
		static void ARStatusList2Message(ARStatusList* pStatus, StringBuilder^ sb)
		{
			if(!pStatus || sb == nullptr)
				return;

			sb->AppendFormat(L"***-- Begin ARStatusList->({0}): ", pStatus->numItems);

			for(int i=0; i<(int)pStatus->numItems; i++)
			{
				// is there a quick way to pin char* to byte[]?
				//char* pMessage = pStatus->statusList[i].messageText;
				//if(!pMessage) pMessage = "";
				//auto buffer = gcnew array<Byte>(strlen(pMessage));
				//for(int j=0; j<buffer->Length; j++)
				//	buffer[j] = pMessage[j];

				//char* pAppendedText = pStatus->statusList[i].appendedText;
				//if(!pAppendedText) pAppendedText = "";
				//auto buffer2 = gcnew array<Byte>(strlen(pAppendedText));
				//for(int j=0; j<buffer2->Length; j++)
				//	buffer2[j] = pAppendedText[j];

				sb->AppendFormat(L" [messageText={0}, appendedText={1}] ", 

					UTF8String2UnicodeString(pStatus->statusList[i].messageText),
					UTF8String2UnicodeString(pStatus->statusList[i].appendedText));


					//System::Text::Encoding::UTF8->GetString(buffer),
					//System::Text::Encoding::UTF8->GetString(buffer2));
			}

			sb->AppendFormat(L"End ARStatusList --*** ");
		}

	private:
		static String^ UTF8String2UnicodeString(char* p)
		{
			// is there a quick way to pin char* to byte[]?
			if(!p)
				return String::Empty;

			int nLen = strlen(p);
			if(nLen == 0)
				return String::Empty;

			auto buffer = gcnew array<Byte>(nLen);

			for(int j=0; j<nLen; j++)
				buffer[j] = p[j];	

			return Encoding::UTF8->GetString(buffer);
		}

	};
}}
